package probe

import (
	"crypto/tls"
	"fmt"
	"log"
	"net/http"
	"net/http/httptrace"
	"simplehttpprobe/global"
	"strings"
	"time"
)

/*
探测顺序
DNSStart:
DNSDone
ConnectStart
ConnectDone
TLSHandshakeStart:
TLSHandshakeDone
GotConn
WroteHeaders
GotFirstResponseByte

*/

// 探测逻辑
func Do_probe(url string) string {
	//超时时间
	twsec := global.Gconfig.HttpProbeTimeout

	dnsStr := ""
	targetAddr := ""

	tlsstatus := false

	// 全局时间
	start := time.Now()

	// 定义好各个时间节点
	var t0, t1, sslt1, sslt2, t2, t3, t4, t5 time.Time
	// 初始化http对象
	req, _ := http.NewRequest("GET", url, nil)

	trace := &httptrace.ClientTrace{
		DNSStart: func(_ httptrace.DNSStartInfo) {
			t0 = time.Now()
			fmt.Println("DNSStart:")
		},
		DNSDone: func(dnsInfo httptrace.DNSDoneInfo) {
			t1 = time.Now()
			// 定义解析切片
			ips := make([]string, 0)
			for _, d := range dnsInfo.Addrs {
				ips = append(ips, d.IP.String())
			}
			dnsStr = strings.Join(ips, ",")
			fmt.Println("DNSDone")
		},

		ConnectStart: func(network, addr string) {
			t2 = time.Now()
			fmt.Println("ConnectStart")
		},
		ConnectDone: func(network, addr string, err error) {
			if err != nil {
				log.Printf("[无法建立和探测目标的连接][addr:%s][err:%v]", addr, err)
				return
			}
			targetAddr = addr
			t3 = time.Now()
			fmt.Println("ConnectDone")

		},
		TLSHandshakeStart: func() {
			sslt1 = time.Now()
			fmt.Println("TLSHandshakeStart:")
		},
		TLSHandshakeDone: func(cs tls.ConnectionState, e error) {
			sslt2 = time.Now()
			tlsstatus = cs.HandshakeComplete
			fmt.Println("TLSHandshakeDone")
		},
		GotConn: func(gci httptrace.GotConnInfo) {
			t4 = time.Now()
			fmt.Println("GotConn")
		},
		GotFirstResponseByte: func() {
			t5 = time.Now()
			fmt.Println("GotFirstResponseByte")
		},
		WroteHeaders: func() {
			fmt.Println("WroteHeaders")
		},
	}
	// 标准用法
	req = req.WithContext(httptrace.WithClientTrace(req.Context(), trace))
	client := http.Client{
		Timeout: time.Duration(twsec) * time.Second,
	}
	resp, err := client.Do(req)
	end := time.Now()
	if err != nil {
		msg := fmt.Sprintf("[http探测出错]\n"+
			"[http探测地址是%s]\n"+
			"[错误详情%s]\n"+
			"[总耗时%s]\n",
			url,
			err,
			time.Now().Sub(start),
		)
		log.Println(msg)
		return msg
	}
	defer resp.Body.Close()

	if t0.IsZero() {
		t1 = t2
		t0 = t2
	}

	dnsLookup := msDurationStr(t1.Sub(t0))
	tlsConnection := msDurationStr(sslt2.Sub(sslt1))
	tcpConnection := msDurationStr(t3.Sub(t2))
	serverProcessing := msDurationStr(t5.Sub(t4))
	total := msDurationStr(end.Sub(t0))
	probeResStr := fmt.Sprintf("[http探测目标%s]\n"+
		"[dns解析目标%s]\n"+
		"[连接的ip和端口%s]\n"+
		"[状态码%d]\n"+
		"[dns解析耗时%s]\n"+
		"[tls握手时间%s]\n"+
		"[tls状态%v]\n"+
		"[tcp连接耗时%s]\n"+
		"[服务器处理总耗时%s]\n"+
		"[总耗时%s]\n",
		url,
		dnsStr,
		targetAddr,
		resp.StatusCode,
		dnsLookup,
		tlsConnection,
		tlsstatus,
		tcpConnection,
		serverProcessing,
		total,
	)

	return probeResStr
}

func msDurationStr(d time.Duration) string {
	return fmt.Sprintf("%dms", int(d/time.Millisecond))
}
